package cn.lili.modules.broadcast.serviceimpl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.SceneEnums;
import cn.lili.common.utils.BeanUtil;
import cn.lili.common.vo.PageVO;
import cn.lili.exchange.AmqpExchangeProperties;
import cn.lili.modules.broadcast.mapper.StudioMapper;
import cn.lili.modules.broadcast.service.CommodityService;
import cn.lili.modules.broadcast.service.StudioCommodityService;
import cn.lili.modules.broadcast.service.StudioService;
import cn.lili.modules.goods.client.GoodsClient;
import cn.lili.modules.goods.entity.dos.Goods;
import cn.lili.modules.goods.entity.dos.Studio;
import cn.lili.modules.goods.entity.dos.StudioCommodity;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
import cn.lili.modules.goods.entity.enums.StudioStatusEnum;
import cn.lili.modules.goods.entity.vos.StudioVO;
import cn.lili.modules.wechat.client.WechatClient;
import cn.lili.modules.wechat.entity.params.WechatParams;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.routing.PromotionRoutingKey;
import cn.lili.trigger.enums.DelayTypeEnums;
import cn.lili.trigger.interfaces.TimeTrigger;
import cn.lili.trigger.message.BroadcastMessage;
import cn.lili.trigger.model.TimeExecuteConstant;
import cn.lili.trigger.model.TimeTriggerMsg;
import cn.lili.trigger.util.DelayQueueTools;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 小程序直播间业务层实现
 *
 * @author Bulbasaur
 * @since 2021/5/17 10:04 上午
 */
@Service
@RequiredArgsConstructor
public class StudioServiceImpl extends ServiceImpl<StudioMapper, Studio> implements StudioService {

    private final WechatClient wechatClient;

    private final StudioCommodityService studioCommodityService;

    private final CommodityService commodityService;

    private final AmqpExchangeProperties amqpExchangeProperties;

    private final TimeTrigger timeTrigger;

    private final GoodsClient goodsClient;

    @Override
    public Boolean create(Studio studio) {
        studio.setStoreId(Objects.requireNonNull(UserContext.getCurrentUser()).getExtendId());
        //创建小程序直播
        Map<String, String> roomMap = wechatClient.create(studio);
        studio.setRoomId(Convert.toInt(roomMap.get("roomId")));
        studio.setQrCodeUrl(roomMap.get("qrcodeUrl"));
        studio.setStatus(StudioStatusEnum.NEW.name());
        //直播间添加成功发送直播间开启、关闭延时任务
        if (this.save(studio)) {
            //直播开启延时任务
            //发送促销活动开始的延时任务
            this.timeTrigger.addDelay(generateMsg(studio, StudioStatusEnum.START));

            //发送促销活动开始的延时任务
            this.timeTrigger.addDelay(generateMsg(studio, StudioStatusEnum.END));
        }
        return true;

    }

    @Override
    public Boolean edit(Studio studio) {
        Studio oldStudio = this.getById(studio.getId());
        wechatClient.editRoom(studio);
        if (this.updateById(studio)) {
            //发送更新延时任务
            //发送促销活动开始的延时任务
            this.timeTrigger.edit(generateMsg(oldStudio, StudioStatusEnum.START), generateMsg(studio, StudioStatusEnum.START));

            //发送促销活动开始的延时任务
            this.timeTrigger.edit(generateMsg(oldStudio, StudioStatusEnum.START), generateMsg(studio, StudioStatusEnum.END));
        }
        return true;
    }

    @Override
    public StudioVO getStudioVO(String id) {
        StudioVO studioVO = new StudioVO();
        Studio studio = this.getById(id);
        //获取直播间信息
        BeanUtil.copyProperties(studio, studioVO);
        //获取直播间商品信息
        studioVO.setCommodityList(commodityService.getCommodityByRoomId(studioVO.getRoomId()));
        return studioVO;
    }

    @Override
    public String getLiveInfo(Integer roomId) {
        Studio studio = this.getByRoomId(roomId);
        //获取直播间并判断回放内容是否为空，如果为空则获取直播间回放并保存
        if (studio.getMediaUrl() != null) {
            return studio.getMediaUrl();
        } else {
            String mediaUrl = wechatClient.getLiveInfo(roomId);
            studio.setMediaUrl(mediaUrl);
            this.save(studio);
            return mediaUrl;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean push(Integer roomId, Integer liveGoodsId, String storeId, String goodsId) {

        //判断直播间是否已添加商品
        if (studioCommodityService.getOne(
                new LambdaQueryWrapper<StudioCommodity>().eq(StudioCommodity::getRoomId, roomId)
                        .eq(StudioCommodity::getGoodsId, liveGoodsId)) != null) {
            throw new ServiceException(ResultCode.STODIO_GOODS_EXIST_ERROR);
        }

        Goods goods = goodsClient.getGoodsByParams(GoodsSearchParams.builder().goodsId(goodsId).storeId(storeId).build());
        if (goods == null) {
            throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
        }

        WechatParams wechatParams = buildWechatParams(roomId, liveGoodsId, storeId);
        //调用微信接口添加直播间商品并进行记录
        if (Boolean.TRUE.equals(wechatClient.pushGoods(wechatParams))) {
            studioCommodityService.save(new StudioCommodity(roomId, liveGoodsId));
            //添加直播间商品数量
            Studio studio = this.getByRoomId(roomId);
            studio.setRoomGoodsNum(studio.getRoomGoodsNum() != null ? studio.getRoomGoodsNum() + 1 : 1);
            //设置直播间默认的商品（前台展示）只展示两个
            if (studio.getRoomGoodsNum() < 3) {
                studio.setRoomGoodsList(JSONUtil.toJsonStr(commodityService.getSimpleCommodityByRoomId(roomId)));
            }
            return this.updateById(studio);
        }
        return false;
    }

    @Override
    public Boolean goodsDeleteInRoom(Integer roomId, Integer goodsId, String storeId) {
        WechatParams wechatParams = buildWechatParams(roomId, goodsId, storeId);
        //调用微信接口删除直播间商品并进行记录
        if (Boolean.TRUE.equals(wechatClient.goodsDeleteInRoom(wechatParams))) {
            studioCommodityService.remove(new QueryWrapper<StudioCommodity>().eq("room_id", roomId).eq("goods_id", goodsId));
            //减少直播间商品数量
            Studio studio = this.getByRoomId(roomId);
            studio.setRoomGoodsNum(studio.getRoomGoodsNum() - 1);
            //设置直播间默认的商品（前台展示）只展示两个
            if (studio.getRoomGoodsNum() < 3) {
                studio.setRoomGoodsList(JSONUtil.toJsonStr(commodityService.getSimpleCommodityByRoomId(roomId)));
            }
            return this.updateById(studio);
        }
        return false;
    }

    private WechatParams buildWechatParams(Integer roomId, Integer goodsId, String storeId) {
        GoodsSearchParams searchParams = new GoodsSearchParams();
        searchParams.setGoodsId(goodsId.toString());
        searchParams.setStoreId(storeId);
        Goods goods = goodsClient.getGoodsByParams(searchParams);
        if (goods == null) {
            throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
        }
        return WechatParams.builder().roomId(roomId).goodsId(goodsId).build();
    }

    @Override
    public Page<StudioVO> studioList(PageVO pageVO, Integer recommend, String status) {
        QueryWrapper queryWrapper = new QueryWrapper<Studio>()
                .eq(recommend != null, "recommend", true)
                .eq(CharSequenceUtil.isNotEmpty(status), "status", status)
                .orderByDesc("create_time");
        if (UserContext.getCurrentUser() != null && UserContext.getCurrentUser().getScene().equals(SceneEnums.STORE)) {
            queryWrapper.eq("store_id", UserContext.getCurrentUser().getExtendId());
        }
        Page page = this.page(PageUtil.initPage(pageVO), queryWrapper);
        List<Studio> records = page.getRecords();
        List<StudioVO> studioVOS = new ArrayList<>();
        for (Studio studio : records) {
            StudioVO studioVO = new StudioVO();
            //获取直播间信息
            BeanUtil.copyProperties(studio, studioVO);
            //获取直播间商品信息
            studioVO.setCommodityList(commodityService.getCommodityByRoomId(studioVO.getRoomId()));
            studioVOS.add(studioVO);
        }
        page.setRecords(studioVOS);
        return page;

    }

    @Override
    public void updateStudioStatus(BroadcastMessage broadcastMessage) {
        this.update(new LambdaUpdateWrapper<Studio>()
                .eq(Studio::getId, broadcastMessage.getStudioId())
                .set(Studio::getStatus, broadcastMessage.getStatus()));
    }

    /**
     * 根据直播间ID获取直播间
     *
     * @param roomId 直播间ID
     * @return 直播间
     */
    private Studio getByRoomId(Integer roomId) {
        return this.getOne(new LambdaQueryWrapper<Studio>().eq(Studio::getRoomId, roomId));
    }

    /**
     * 生成延时任务消息
     *
     * @param studio           直播间
     * @param studioStatusEnum 直播状态
     * @return 延时任务消息
     */
    private TimeTriggerMsg generateMsg(Studio studio, StudioStatusEnum studioStatusEnum) {

        BroadcastMessage broadcastMessage = new BroadcastMessage(studio.getId(), studioStatusEnum.name());
        TimeTriggerMsg timeTriggerMsg = TimeTriggerMsg.builder()
                .triggerTime(Long.parseLong(studioStatusEnum.equals(StudioStatusEnum.START) ? studio.getStartTime() : studio.getEndTime()) * 1000L)
                .exchange(amqpExchangeProperties.getPromotion())
                .param(broadcastMessage)
                .uniqueKey(DelayQueueTools.wrapperUniqueKey(DelayTypeEnums.BROADCAST, studio.getId()))
                .routingKey(PromotionRoutingKey.BROADCAST)
                .triggerExecutor(TimeExecuteConstant.BROADCAST_EXECUTOR)
                .build();

        return timeTriggerMsg;

    }

}
